﻿Imports Ionic.Zip
Imports System.Xml
Imports System.IO
Imports System.Xml.Serialization
Imports ProgettoSicurezza.CertificationAuthority.MyCertificateManager
Imports Org.BouncyCastle.X509
Imports Org.BouncyCastle.Security
Imports Org.BouncyCastle.Crypto.Parameters
Imports System.Collections.ObjectModel
Imports System.Text
Imports Org.BouncyCastle.Pkcs
Imports SharedTools
Imports Org.BouncyCastle.Asn1.Pkcs

Public Class PersistenceManager
    Public Shared ReadOnly Property IsDataPresent As Boolean
        Get
            Dim CApath = FileSystemUtilities.AppPath & "\ProgettoSicurezza\CA"
            Return File.Exists(CApath & "\cert.xml") AndAlso File.Exists(CApath & "\CAdata.zip")
        End Get
    End Property

#Region "Loaders"
    Public Shared Sub LoadAll()
        If Not IsDataPresent Then
            Throw New DataNotPresentException
        End If

        Dim pass = Repository.Password

        'load the certificate
        Try
            LoadCertificate()
        Catch ex As CertificateExpiredException
            Repository.Data = New CAdata
            Throw New CertificateExpiredException
        End Try


        'load other data
        Try
            Dim CApath = FileSystemUtilities.AppPath & "\ProgettoSicurezza\CA"
            Using zip As New ZipFile(CApath & "\CAdata.zip")
                zip.Password = pass
                For Each item In zip
                    Select Case item.FileName
                        Case "data.xml"
                            Dim stream As New IO.MemoryStream
                            item.Extract(stream)
                            stream.Position = 0
                            Using sr As New StreamReader(stream)
                                LoadCAdata(sr.ReadToEnd)
                            End Using
                            stream.Close()

                        Case "requests.xml"
                            Dim s As New XmlSerializer(GetType(List(Of CertificateRequestItem)))
                            Dim stream As New IO.MemoryStream
                            item.Extract(stream)
                            stream.Position = 0
                            Using sr As New StreamReader(stream)
                                Dim reader As New XmlTextReader(sr)
                                Repository.Requests.Clear()
                                Dim requests = DirectCast(s.Deserialize(reader), List(Of CertificateRequestItem))
                                For Each req In requests
                                    Repository.Requests.Add(req)
                                Next
                            End Using
                            stream.Close()

                        Case "certificates.xml"
                            Dim s As New XmlSerializer(GetType(List(Of CertificateItem)))
                            Dim stream As New IO.MemoryStream
                            item.Extract(stream)
                            stream.Position = 0
                            Using sr As New StreamReader(stream)
                                Dim reader As New XmlTextReader(sr)
                                Repository.Certificates.Clear()
                                Dim certs = DirectCast(s.Deserialize(reader), List(Of CertificateItem))
                                For Each req In certs
                                    Repository.Certificates.Add(req)
                                Next
                            End Using
                            stream.Close()

                        Case "prvKey.xml"
                            Dim stream As New IO.MemoryStream
                            item.Extract(stream)
                            stream.Position = 0
                            Using sr As New StreamReader(stream)
                                Dim value = XmlTool.GetValue("PrivateKey", sr.ReadToEnd)
                                'Dim bytes = Convert.FromBase64String(value)
                                Dim bytes = Convert.FromBase64String(value)
                                Repository.Data.BouncyCastlePrivateKey = DirectCast(PrivateKeyFactory.DecryptKey(Repository.Password.ToCharArray, bytes), RsaPrivateCrtKeyParameters)
                            End Using
                            stream.Close()

                        Case "CRL.xml"
                            Dim stream As New IO.MemoryStream
                            item.Extract(stream)
                            stream.Position = 0
                            Using sr As New StreamReader(stream)
                                Dim value = XmlTool.GetValue("CRL", sr.ReadToEnd)
                                Dim bytes = Convert.FromBase64String(value)
                                Dim crlparser As New X509CrlParser
                                Repository.CRL = crlparser.ReadCrl(bytes)
                            End Using
                            stream.Close()
                    End Select
                Next
            End Using
        Catch ex As BadPasswordException
            Repository.ResetAll()
            Throw ex
        End Try

    End Sub


    Private Shared Sub LoadCAdata(data As String)
        Dim doc As New XmlDocument()
        doc.LoadXml(data)
        Dim node = doc.GetElementsByTagName("PrivateKey")
        Dim keybytes = Convert.FromBase64String(node(0).InnerText)
        Dim key = DirectCast(PrivateKeyFactory.CreateKey(keybytes), RsaPrivateCrtKeyParameters)

        Repository.Data.BouncyCastlePrivateKey = key
    End Sub

    Public Shared Sub LoadCertificate()
        Dim CApath = FileSystemUtilities.AppPath & "\ProgettoSicurezza\CA"
        If Not File.Exists(CApath & "\cert.xml") Then
            Throw New DataNotPresentException
        End If

        Dim doc As New XmlDocument()
        doc.Load(CApath & "\cert.xml")
        Dim encoded = doc.GetElementsByTagName("Certificate")(0).InnerText
        Dim bytes = Convert.FromBase64String(encoded)

        Dim gen As New X509CertificateParser()
        Dim cert = gen.ReadCertificate(bytes)

        If cert.NotAfter < DateTime.Now Then
            Throw New CertificateExpiredException
        End If

        Repository.Data.MyCertificate = cert
    End Sub
#End Region

#Region "Savers"
    Public Shared Sub SaveAll()
        SaveMyCertificate()
        SaveCAData()
        SaveRequests()
        SaveCertificates()
    End Sub

    Public Shared Sub SaveCAData()
        Dim data = Repository.Data

        Dim rand As New SecureRandom
        Dim arr As Byte() = New Byte(7) {}
        'rand.NextBytes(arr)

        Dim privateKeyInfo As EncryptedPrivateKeyInfo = EncryptedPrivateKeyInfoFactory.CreateEncryptedPrivateKeyInfo("PBEwithSHA-1and3-keyDESEDE-CBC", Repository.Password.ToCharArray, arr, 3, data.BouncyCastlePrivateKey)
        Dim bytes = privateKeyInfo.GetEncoded

        Dim ms As New StringWriter
        Dim w As New XmlTextWriter(ms)
        w.WriteStartDocument()
        w.WriteStartElement("PrivateKey")
        w.WriteBase64(bytes, 0, bytes.Count)
        'w.WriteBinHex(bytes, 0, bytes.Count)
        w.WriteEndElement()
        w.WriteEndDocument()
        w.Close()

        Dim CApath = FileSystemUtilities.AppPath & "\ProgettoSicurezza\CA"
        Using zip As New ZipFile(CApath & "\CAdata.zip")
            zip.Password = Repository.Password
            zip.Encryption = EncryptionAlgorithm.WinZipAes256

            zip.UpdateEntry("prvKey.xml", ms.ToString)
            zip.Save()
        End Using
    End Sub


    Public Shared Sub SaveMyCertificate()
        Dim cert = Repository.Data.MyCertificate

        FileSystemUtilities.CheckAndCreateDirectories()
        Dim CApath = FileSystemUtilities.AppPath & "\ProgettoSicurezza\CA"

        Dim w As New StreamWriter(CApath & "\cert.xml")
        Using xw As New XmlTextWriter(w)
            xw.WriteStartDocument()

            xw.WriteStartElement("Certificate")
            xw.WriteBase64(cert.GetEncoded(), 0, cert.GetEncoded.Count)
            xw.WriteEndElement()

            xw.WriteEndDocument()
        End Using
        w.Close()
    End Sub

    Public Shared Sub SaveRequests()
        Dim s As New XmlSerializer(GetType(List(Of CertificateRequestItem)))
        Dim ms As New StringWriter
        s.Serialize(ms, Repository.Requests.ToList)


        Dim CApath = FileSystemUtilities.AppPath & "\ProgettoSicurezza\CA"
        Using zip As New ZipFile(CApath & "\CAdata.zip")
            zip.Password = Repository.Password
            zip.Encryption = EncryptionAlgorithm.WinZipAes256

            zip.UpdateEntry("requests.xml", ms.ToString)
            zip.Save()
        End Using

        ms.Close()
    End Sub

    Public Shared Sub SaveCertificates()
        Dim s As New XmlSerializer(GetType(List(Of CertificateItem)))
        Dim ms As New StringWriter
        s.Serialize(ms, Repository.Certificates.ToList)


        Dim CApath = FileSystemUtilities.AppPath & "\ProgettoSicurezza\CA"
        Using zip As New ZipFile(CApath & "\CAdata.zip")
            zip.Password = Repository.Password
            zip.Encryption = EncryptionAlgorithm.WinZipAes256

            zip.UpdateEntry("certificates.xml", ms.ToString)
            zip.Save()
        End Using

        ms.Close()
    End Sub

    Public Shared Sub SaveCRL()
        If Repository.CRL Is Nothing Then
            Return
        End If

        Dim bytes = Repository.CRL.GetEncoded

        Dim ms As New StringWriter
        Dim w As New XmlTextWriter(ms)
        w.WriteStartDocument()
        w.WriteStartElement("CRL")
        w.WriteBase64(bytes, 0, bytes.Count)
        w.WriteEndElement()
        w.WriteEndDocument()
        w.Close()

        Dim CApath = FileSystemUtilities.AppPath & "\ProgettoSicurezza\CA"
        Using zip As New ZipFile(CApath & "\CAdata.zip")
            zip.Password = Repository.Password
            zip.Encryption = EncryptionAlgorithm.WinZipAes256

            zip.UpdateEntry("CRL.xml", ms.ToString)
            zip.Save()
        End Using
    End Sub
#End Region


    Public Class DataNotPresentException
        Inherits Exception
    End Class
    Public Class CertificateExpiredException
        Inherits Exception
    End Class
End Class
